package edu.mercer.sse.bkp;

import java.nio.file.Path;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Future;

import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.base.Throwables;
import com.google.common.collect.Collections2;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.ListenableFuture;

import static com.google.common.base.Preconditions.checkArgument;

/**
 * Run compression for a path argument provided.<p> 
 * 
 * Uses Command and its extended interfaces for progress, undo. Demonstration
 * 	of Composite of Command showing overall percentage completed.<p>
 * 
 * Accepts '--verbose' argument, path(s), and displays usage help.<p>
 */

public class CompressAppWithCommand implements ConfigurationConstants, 
											   AutoCloseable
{	
	public static void main( final String[] args ) 
	{		
		try
		{
			Settings.access().setProperty("SLOW", "0");
			Settings.access().setProperty("VALIDATION", "false");
			
			if( args.length < 1 ) halt( HELP );
			
			Boolean _verbose = Arrays.asList(args)
							 		 .contains("--verbose");
			
			final Iterator<Path> filesFound = GetFilesCommand.factory(args)
														 	 .execute()
														 	 .iterator();

			CompressCommandComposite taskComposite = CompressCommandComposite
											 		 .factory();
			
			List<CompressCommand> cmds = Lists.newArrayList();
			
			checkArgument( filesFound.hasNext(), 
					"no files found for compression" );
			
			while( filesFound.hasNext() ) 
			{
				cmds.add( CompressCommand.factory()
				 						 .withTask(
				 								 
				 		SnappyCompression.builder()
				 						 .path( filesFound.next())
				 						 .build() ) );
				
			}//while
			
			taskComposite.add( ImmutableList.copyOf(cmds) );
			
			checkArgument( taskComposite.getChildren()
										.size() > 0, 
					"no files found for compression" );

			LinkedList<ListenableFuture<TaskResults
											<ImmutableMap<String, String>>>> 
				taskFutures = Lists.newLinkedList( taskComposite.execute() );

			if( !_verbose )
			{
				/* Example of showing overall progress meter */
				
				while( taskComposite.progress() < 1F ) 
					System.out.println( taskComposite.textProgressMeter() );

				System.out.println( taskComposite.textProgressMeter() );
			}
			else
			{
				/* 
				 * Example of showing CommandComposite: access Command 
				 * 	Pattern Leaves, individual Command's:
				 *  
				 * 	- Asynchronous File compression progress
				 */

				List<String> shown = Lists.newArrayList();

				String pastMeter="";
				
				while( taskComposite.progress() < 1F )
				{					
					for( String meter : Lists.transform( 
												taskComposite.getChildren(), 
												TasksRunning.factory() ) )
					{
						if( ! shown.contains(meter) && 
							! pastMeter.contentEquals(meter) )
						{ 
							System.out.println(meter);
						
							if(meter.endsWith("100.0%"))shown.add(meter);
						
							pastMeter = meter;
						
						}//if
						
						if(Thread.interrupted()) break;

					}//for
					
					if(Thread.interrupted()) halt();
					
				}//while
				
			}//if
			
			while( taskFutures.size() > 0 )
			{ 
				TaskResults<ImmutableMap<String, String>> results = 
						taskFutures.pop().get();
				
				if( _verbose ) results.print();

			}//while
			
		}
		catch( final BackupSettingsException err )
		{
			err.accept( BackupErrorLoggingVisitor.factory() );			
			
			throw err;
			
		}catch( final Throwable err ){
			
			final BackupThrowable errExposed = new BackupThrowable( err );
			
			errExposed.accept( BackupErrorLoggingVisitor.factory() );
			
			Throwables.propagate( errExposed );
			
		}
		finally
		{
			halt();

		}//try
		
	}//method


	public static void halt()
	{
		halt( 0, "" );
		
	}//method
	

	public static void halt( final String msg )
	{
		halt( 0, msg );
		
	}//method

	
	public static void halt( final Integer returnCode, final String msg )
	{
		if( msg.length() > 0 ) System.out.println( msg );
		
		CompressCommand.shutdownExecutor();
		
		System.exit( returnCode );
		
	}//method

	
	public void close()
	{
		halt();
		
	}//method
	
	
}//class


/**
 * Return progress meter for each asynchronous file compression task.<p>
 *
 * @see Function
 */

class TasksRunning implements Function<CompressCommand, String>
{
	/** 
	 * CompressionInProgress no-arg constructor.<p>
	 * 
	 * For use by factory.<p>
	 *  
	 * @see
	 */
	
	private TasksRunning()
	{
		super();
		
	}//constructor
	
	
	/**
	 * CompressionInProgress Factory.<p>
	 * 
	 * @return CompressionInProgress new instance
	 */
	
	static TasksRunning factory()
	{
		return new TasksRunning();
		
	}//method
	

	@Override
	public String apply( final CompressCommand task ) 
	{		
		return task.textProgressMeter();
		
	}//method

	
}//class